#include "SoundPotSoundHost.h"

SoundPot::SoundHost::SoundHost(InstrumentDao* instrumentDao, InstrumentResponder* responder)
{
	this->instrumentDao = instrumentDao;
	this->responder = responder;
	loadedInstruments = new std::list<Instrument*>();
}

SoundPot::SoundHost::~SoundHost()
{
	// Remove and delete all loaded instruments

	std::list<Instrument*>::iterator iterator;

	for(iterator = loadedInstruments->begin(); iterator != loadedInstruments->end(); iterator++)
	{
		delete (*iterator);
	}
	loadedInstruments->clear();

	
	// Delete list

	delete loadedInstruments;
}

/**	Adds a sound to the host and loads it.
	Thereby it tries to find an already available, free instrument.
	If it doesn't find any and instruments are allowed to change, it creates one.
	Todo: Otherwise it throws an exception.
*/
void SoundPot::SoundHost::addSound(Sound* sound)
{
	Instrument* instrumentToAssign;
	
	instrumentToAssign = getUnemployedInstrument(sound->getInstrumentID());

	sound->assignInstrument(instrumentToAssign);
	
	sound->load();
}

/** Removes a sound from the host.
	Sometimes it also removes the corresponding instrument.
*/
void SoundPot::SoundHost::removeSound(Sound* sound)
{
	Instrument* assignedInstrument = sound->getAssignedInstrument();
	sound->assignInstrument(NULL);

	// Remove instrument if it isn't marked as permanent and instruments are allowed to change
	if(instrumentsMayChange() && ! assignedInstrument->isPermanent())
	{
		removeAndDeleteInstrument(assignedInstrument);
	}
}

/** Replaces a sound in the host with a new one.
	This can be more effective than just to remove and add a sound independently.
*/
void SoundPot::SoundHost::replaceSound(Sound* oldSound, Sound* newSound)
{
	Instrument* assignedInstrument;
	
	// Todo: Throw exception: Old sound is not existing!
	if(oldSound->getInstrumentID() == newSound->getInstrumentID())	// Sounds use the same instrument
	{
		// Simply change sound on the already available instrument

		assignedInstrument = oldSound->getAssignedInstrument();

		oldSound->assignInstrument(NULL);
	
		newSound->assignInstrument(assignedInstrument);

		newSound->load();
	}
	else	// Sounds use different instruments
	{
		// Remove and add like usually
		removeSound(oldSound);
		addSound(newSound);
	}

}

// Todo: separate into methods assignInstrumentsToSounds and loadSounds
void SoundPot::SoundHost::replaceAllSounds(std::list<Sound*>& newSounds)
{

	std::list<Instrument*>::iterator instrumentIterator;
	std::list<Sound*>::iterator newSoundIterator;

	/*	For every currently loaded instruments and for every new sound:
	 	Unemploy instrument and employ instrument with new sound if it's possible.
	*/


	for(instrumentIterator = loadedInstruments->begin(); instrumentIterator != loadedInstruments->end(); instrumentIterator++)
	{
		//	Todo: Improve this so that even the sounds itself haven't to be loaded again if they are the same???

		if((*instrumentIterator)->isEmployed())
		{
			(*instrumentIterator)->assignSound(NULL);
		}
		
		for(newSoundIterator = newSounds.begin(); newSoundIterator != newSounds.end(); newSoundIterator++)
		{
			if(! (*newSoundIterator)->isAssigned()
				&& (*instrumentIterator)->getInstrumentID() == (*newSoundIterator)->getInstrumentID() )
				// New sound has no instrument assigned yet and current instrument is suited
			{
				// Employ this instrument with the sound
				(*newSoundIterator)->assignInstrument((*instrumentIterator));
				(*newSoundIterator)->load();
			}
		}
	}


	/*	Here every sound, which had luck and found a suited instrument already loaded, is assigned to that instrument
		So now we have to care for the rest. Simply iterate through all sounds not assigned yet and add them regularly.
	*/
	Instrument* newInstrument;

	for(newSoundIterator = newSounds.begin(); newSoundIterator != newSounds.end(); newSoundIterator++)
	{
		if(! (*newSoundIterator)->isAssigned())
		{
			newInstrument = createAndAddInstrument((*newSoundIterator)->getInstrumentID());
			(*newSoundIterator)->assignInstrument(newInstrument);
			(*newSoundIterator)->load();
		}
	}

	//	Still one thing to do: Remove old, unused instruments which are not marked as permanent.

	for(instrumentIterator = loadedInstruments->begin(); instrumentIterator != loadedInstruments->end(); instrumentIterator++)
	{
		if(! (*instrumentIterator)->isEmployed() && ! (*instrumentIterator)->isPermanent())
		{
			removeAndDeleteInstrument((*instrumentIterator));
		}
	}
	
	//	That's it!
	
}




SoundPot::Instrument* SoundPot::SoundHost::createAndAddInstrument(const std::string& instrumentID)
{
	Instrument* newInstrument = instrumentDao->findInstrument(instrumentID);

	// Todo: Maybe load instrument not here but at a later time
	newInstrument->load();

	newInstrument->assignSound(NULL); // Make sure no sound is loaded

	loadedInstruments->push_back(newInstrument);

	newInstrument->setResponder(this);

	return newInstrument;
}

void SoundPot::SoundHost::removeAndDeleteInstrument(Instrument* instrument)
{
	if(! instrument->isEmployed())
	{
		loadedInstruments->remove(instrument);
		delete instrument;
	}
}




SoundPot::Instrument* SoundPot::SoundHost::getUnemployedInstrument(const std::string& instrumentID)
{
	Instrument* unemployedInstrument = NULL;

	std::list<Instrument*>::iterator iterator;

	for(iterator = loadedInstruments->begin(); iterator != loadedInstruments->end(); iterator++)
	{
		if(! (*iterator)->isEmployed() && (*iterator)->getInstrumentID() == instrumentID)
		{
			unemployedInstrument = (*iterator);
			break;
		}
	}

	if(! unemployedInstrument) // No unemployed instrument of specified type available
	{
		if(instrumentsMayChange())
		{
			unemployedInstrument = createAndAddInstrument(instrumentID);
		}
		else
		{
			// Todo: Throw "necessary instrument not loaded" exception
		}
	}
	return unemployedInstrument;
}



void SoundPot::SoundHost::suspendAllSounds()
{
	// Todo
}

void SoundPot::SoundHost::wakeUpAllSounds()
{
	// Todo
}

void SoundPot::SoundHost::setSampleRate(const float& value)
{
	std::list<Instrument*>::iterator iterator;

	for(iterator = loadedInstruments->begin(); iterator != loadedInstruments->end(); iterator++)
	{
		(*iterator)->setSampleRate(value);
	}
}

void SoundPot::SoundHost::setMaxBlockSize(const int& value)
{
	std::list<Instrument*>::iterator iterator;

	for(iterator = loadedInstruments->begin(); iterator != loadedInstruments->end(); iterator++)
	{
		(*iterator)->setMaxBlockSize(value);
	}
}



float SoundPot::SoundHost::getCurrentSampleRate()
{
	return responder->getCurrentSampleRate();
}

int SoundPot::SoundHost::getMaxBlockSize()
{
	return responder->getMaxBlockSize();
}